using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MSRI.HelperClasses;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data;
using Instedd.Labs.Geo.Library.Kml;
using Instedd.Labs.Geo.Library.Sms;
using Instedd.Labs.Geo.Library.Tests.GPS;
using System.Collections.Specialized;

namespace Instedd.Labs.Geo.Library.Tests.Managers
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class SmsMessagesManagerFixture
    {
        static Database db;
        /// <summary>
        /// This database instance should not be changed by the tests.
        /// </summary>
        static Database readOnlyDb;

        SmsMessagesManager manager;

        Configuration configuration;

        public SmsMessagesManagerFixture()
        {}

        [ClassInitialize()]
        public static void ClassInitialize(TestContext testContext) 
        {
            db = DatabaseFactory.CreateDatabase("SMS.VE.MessagesDatabase");
            readOnlyDb = DatabaseFactory.CreateDatabase("SMS.VE.MessagesDatabase.ReadOnly");
        }

        [TestInitialize]
        public void TestInitialize()
        {
            configuration = new Configuration();
            configuration.LocalNumber = "Local";
            configuration.DefaultLocation = "37.477719 -122.150593";
            manager = new SmsMessagesManager(db, new MockSenderNameLookupProvider(), new MockGeoCoder(), configuration);
        }

        [TestMethod]
        public void SaveAndGetSavedMessage()
        {
            SmsClass sms = new SmsClass();
            sms.Time = DateTime.Now;
            sms.MessageID = Guid.NewGuid();
            sms.Number = "1144556677";
            SmsExtension smsExtension = new SmsExtension(sms, new LatLong(12.12, 34.34), "testing message", "testing sender", "4444");

            manager.Save(smsExtension);

            Assert.IsNotNull(manager.GetMessage(delegate(SmsExtension s) { return s.Sms.MessageID.Equals(sms.MessageID); }));
        }

        [TestMethod]
        public void GetAllMessagesReturnsAll()
        {
            manager = new SmsMessagesManager(readOnlyDb, new MockSenderNameLookupProvider(), new MockGeoCoder(), configuration);

            Assert.AreEqual(4, new List<SmsExtension>(manager.GetAllMessages()).Count);
        }

        [TestMethod]
        public void GetExistentMessageByIdReturnsTheMessage()
        {
            SmsExtension message = manager.GetMessageById("BE08AB5F-C8E2-4603-AA12-9FB7402A44F7");

            Assert.IsNotNull(message);
        }

        [TestMethod]
        public void GetExistentMessageBySenderNumberReturnsAllMesagesOfTheSenderNumber()
        {
            SmsMessagesManager manager = new SmsMessagesManager(readOnlyDb, new MockSenderNameLookupProvider(), new MockGeoCoder(), configuration);

            IEnumerable<SmsExtension> messages = manager.GetMessagesBySenderNumber("98765");

            Assert.IsNotNull(messages);

            List<SmsExtension> messagesList = new List<SmsExtension>(messages);

            Assert.AreEqual(2, messagesList.Count);
        }

        [TestMethod]
        public void ValidLatLongAndMessageWithCustomProviderCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("1150001234", "20.12*55.11*sms message text"));

            Assert.AreEqual(sms.Message, "sms message text");
            Assert.AreEqual(20.12, sms.Location.Lat);
            Assert.AreEqual(-55.11, sms.Location.Long);
        }

        [TestMethod]
        public void ValidLatLongAndMessageWithDefaultProviderCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("1150001234", "20.12*55.11*sms message text"));

            Assert.AreEqual(sms.Message, "sms message text");
            Assert.AreEqual(20.12, sms.Location.Lat);
            Assert.AreEqual(-55.11, sms.Location.Long);
        }

        [TestMethod]
        public void ValidLatLongAndEmptyMessageCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("1150001234", "20.12*55.11*"));

            Assert.AreEqual(sms.Message, string.Empty);
            Assert.AreEqual(20.12, sms.Location.Lat);
            Assert.AreEqual(-55.11, sms.Location.Long);
        }

        [ExpectedException(typeof(InvalidBodyFormatException))]
        [TestMethod]
        public void InvalidLatitudeThrowsInvalidFormat()
        {
            SmsExtension sms = manager.Create(new SmsClass("1150001234", "invalidLat*55.11*sms message text"));
        }

        [ExpectedException(typeof(InvalidBodyFormatException))]
        [TestMethod]
        public void InvalidLongitudeThrowsInvalidFormat()
        {
            SmsExtension sms = manager.Create(new SmsClass("1150001234", "20.12*invalidLong*sms message text"));
        }

        [TestMethod]
        public void MessageWithoutLocationCreatesSmsExtensionUsingTheDefaultLocationIfTheLastWellKnownLocationCouldNotBeDetermined()
        {
            SmsExtension sms = manager.Create(new SmsClass("45454545", "first message without location must use the default location"));

            Assert.AreEqual("first message without location must use the default location", sms.Message);
            Assert.AreEqual(37.477719, sms.Location.Lat);
            Assert.AreEqual(-122.150593, sms.Location.Long);
        }

        [ExpectedException(typeof(InvalidBodyFormatException))]
        [TestMethod]
        public void MessageWithoutLocationThrowsIfTheDefaultLocationWasNotConfiguredAndTheLastWellKnownLocationCouldNotBeDetermined()
        {
            SmsMessagesManager manager = new SmsMessagesManager(db, new MockSenderNameLookupProvider(), new MockGeoCoder(), new Configuration());

            SmsExtension sms = manager.Create(new SmsClass("45454545", "first message without location must use the default location"));
        }

        [TestMethod]
        public void MessageWithoutLocationCreatesSmsExtensionMessageUsingTheLatestLocationOfTheSenderName()
        {
            SmsMessagesManager manager = new SmsMessagesManager(readOnlyDb, new MockSenderNameLookupProvider(), new MockGeoCoder(), this.configuration);
            
            SmsExtension sms = manager.Create(new SmsClass("98765", "another message from the latest location"));

            Assert.AreEqual(new LatLong(22.22, 66.66), sms.Location);
        }

        [TestMethod]
        public void LatLongUsingCommasInsteadOfPoinstCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("8484", "33,33*44,55*message"));

            Assert.AreEqual(new LatLong(33.33, -44.55), sms.Location);
            Assert.AreEqual("message", sms.Message);
        }

        [TestMethod]
        public void ForgettingLastAsteriskAndHavingNothingCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("8484", "33,33*44,55message"));

            Assert.AreEqual(new LatLong(33.33, -44.55), sms.Location);
            Assert.AreEqual("message", sms.Message);
        }

        [TestMethod]
        public void ForgettingLastAsteriskAndSpaceCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("8484", "33,33*44,55 message"));

            Assert.AreEqual(new LatLong(33.33, -44.55), sms.Location);
            Assert.AreEqual("message", sms.Message);
        }

        [TestMethod]
        public void SpacesAroundAsterisksCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("8484", "33,33 * 44,55 * message"));

            Assert.AreEqual(new LatLong(33.33, -44.55), sms.Location);
            Assert.AreEqual("message", sms.Message);
        }

        [TestMethod]
        public void LatLongWithDMSFormatCreatesSmsExtension()
        {
            SmsExtension sms = manager.Create(new SmsClass("8484", "-37.28.39.79*-122.9.2.13*message"));

            Assert.AreEqual(new LatLong(-37.477719, -122.150592), sms.Location);
            Assert.AreEqual("message", sms.Message); 
        }

        [TestMethod]
        public void GetAnsweredMessageReturnsAMessageWithTheAnsweredMessage()
        {
            SmsClass sms = new SmsClass("55558888", "12*12*message");

            SmsExtension message = manager.Create(sms);
            SmsExtension answer = message.Reply("44443333", "message reply");

            manager.Save(message);
            manager.Save(answer);

            SmsExtension loadedAnswer = manager.GetMessageById(answer.GetMessageIdAsString());

            Assert.IsNotNull(loadedAnswer);
            Assert.IsNotNull(loadedAnswer.AnsweredMessage);
            Assert.AreEqual(loadedAnswer.Sms.MessageID, answer.Sms.MessageID);
            Assert.AreEqual(message.Sms.MessageID, message.Sms.MessageID);
        }

        [TestMethod]
        public void ForgettingMinusOnLongitudeCreatesSmsExtensionMessageAndConvertTheLongitudeValueToNegative()
        {
            SmsClass sms = new SmsClass("55558888", "11.22*33.44*message");

            SmsExtension message = manager.Create(sms);

            Assert.IsNotNull(message);
            Assert.AreEqual(11.22, message.Location.Lat);
            Assert.AreEqual(-33.44, message.Location.Long);
        }

        [TestMethod]
        public void NotForgettingMinusOnLongitudeCreatesSmsExtension()
        {
            SmsClass sms = new SmsClass("55558888", "11.22*-33.44*message");

            SmsExtension message = manager.Create(sms);

            Assert.IsNotNull(message);
            Assert.AreEqual(11.22, message.Location.Lat);
            Assert.AreEqual(-33.44, message.Location.Long);
        }

        [TestMethod]
        public void MessageWithPostalAddressInsteadOfLatLongCreatesMessageAndRevolveLatLongUsingGeoCoderServices()
        {
            SmsClass sms = new SmsClass("55558888", "4040 54th st,Redmond,WA,4444*message");

            SmsExtension message = manager.Create(sms);

            Assert.IsNotNull(message);
            Assert.AreEqual("message", message.Message);
            Assert.AreEqual(12.22, message.Location.Lat);
            Assert.AreEqual(44.55, message.Location.Long);
        }

        [TestMethod]
        public void MessageWithInvalidPostalAddressReturnsLastWellKnownLocationOfTheSenderNumber()
        {
            SmsMessagesManager manager = new SmsMessagesManager(readOnlyDb, new MockSenderNameLookupProvider(), new MockGeoCoder(), this.configuration);

            SmsExtension sms = manager.Create(new SmsClass("98765", "invalid postal address*message"));

            Assert.AreEqual("message", sms.Message);
            Assert.AreEqual(new LatLong(22.22, 66.66), sms.Location);
        }

        [TestMethod]
        public void MessageWithInvalidPostalAddressAndNonExistentLastWellKnownLocationReturnsDefaultLocation()
        {
            Configuration configuration = new Configuration();
            configuration.DefaultLocation = "11.22 66.77";

            SmsMessagesManager manager = new SmsMessagesManager(readOnlyDb, new MockSenderNameLookupProvider(), new MockGeoCoder(), configuration);

            SmsExtension sms = manager.Create(new SmsClass("999111", "invalid postal address*message"));

            Assert.AreEqual("message", sms.Message);
            Assert.AreEqual(new LatLong(11.22, 66.77), sms.Location);
        }

        [ExpectedException(typeof(InvalidBodyFormatException))]
        [TestMethod]
        public void MessageWithInvalidPostalAddressAndNonExistentLastWellKnownLocationAndNonExistentDefaultLocationThrows()
        {
            Configuration configuration = new Configuration();
            configuration.DefaultLocation = null;

            SmsMessagesManager manager = new SmsMessagesManager(readOnlyDb, new MockSenderNameLookupProvider(), new MockGeoCoder(), configuration);

            SmsExtension sms = manager.Create(new SmsClass("999111", "invalid postal address*message"));
        }
    }
}